From 05abae56d46b997c272533f0d47e5ab4358d5947 Mon Sep 17 00:00:00 2001 From: oliskoli Date: Sun, 26 Nov 2006 19:39:23 +0000 Subject: [PATCH] Add support for "Raymarine Waypoint File". --- Makefile.in | 4 +- raymarine.c | 474 +++++++++++++++++++++++++++++++++ reference/raymarine-sample.gpx | 83 ++++++ reference/raymarine-sample.rwf | 164 ++++++++++++ testo | 6 + vecs.c | 7 + xmldoc/formats/raymarine.xml | 7 + 7 files changed, 744 insertions(+), 1 deletion(-) create mode 100644 raymarine.c create mode 100644 reference/raymarine-sample.gpx create mode 100644 reference/raymarine-sample.rwf create mode 100644 xmldoc/formats/raymarine.xml diff --git a/Makefile.in b/Makefile.in index 1294a981f..054a1911a 100644 --- a/Makefile.in +++ b/Makefile.in @@ -54,7 +54,7 @@ FMTS=magproto.o gpx.o geo.o mapsend.o mapsource.o garmin_tables.o \ tef_xml.o maggeo.o pathaway.o vitosmt.o gdb.o bcr.o coto.o \ ignrando.o stmwpp.o msroute.o cst.o nmn4.o mag_pdb.o compegps.o \ yahoo.o unicsv.o wfff_xml.o garmin_txt.o axim_gpb.o gpssim.o \ - wbt-200.o stmsdf.o gtrnctr.o dmtlog.o + wbt-200.o stmsdf.o gtrnctr.o dmtlog.o raymarine.o FILTERS=position.o radius.o duplicate.o arcdist.o polygon.o smplrout.o \ reverse_route.o sort.o stackfilter.o trackfilter.o discard.o \ @@ -553,6 +553,8 @@ quovadis.o: quovadis.c quovadis.h defs.h config.h queue.h gbtypes.h \ coldsync/palm.h coldsync/../gbtypes.h coldsync/pdb.h radius.o: radius.c defs.h config.h queue.h gbtypes.h zlib/zlib.h \ zlib/zconf.h gbfile.h cet.h cet_util.h inifile.h filterdefs.h grtcirc.h +raymarine.o: raymarine.c defs.h config.h queue.h gbtypes.h cet.h cet_util.h \ + inifile.h filterdefs.h grtcirc.h gbfile.h csv_util.h reverse_route.o: reverse_route.c defs.h config.h queue.h gbtypes.h \ zlib/zlib.h zlib/zconf.h gbfile.h cet.h cet_util.h inifile.h \ filterdefs.h diff --git a/raymarine.c b/raymarine.c new file mode 100644 index 000000000..b516fd083 --- /dev/null +++ b/raymarine.c @@ -0,0 +1,474 @@ + /* + + Support for Raymarine Waypoint File (.rwf). + + Copyright (C) 2006 Olaf Klein, o.b.klein@gpsbabel.org + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA +*/ + +/* + Known format limits: + + Waypoint name: max. 16 characters + Routes: max. 50 waypoints per route + + History: + + 2006/10/30: Initial release (not yet in GPSBabel source tree) + 2006/11/08: +*/ + +#include "defs.h" +#include "inifile.h" +#include "csv_util.h" + +#include +#include +#include + +typedef unsigned long long guid_t; + +static inifile_t *fin; +static gbfile *fout; +static waypoint **depot; +static short_handle hshort; +static int size_of_depot, items_in_depot; +static int rte_index, rte_wpt_index; +static char *sguid; +static char *opt_location; + +#define MYNAME "raymarine" + +static +arglist_t raymarine_args[] = +{ + { "location", &opt_location, "Default location", "New location", ARGTYPE_STRING, ARG_NOMINMAX }, + ARG_TERMINATOR +}; + +/* from csv_util.c: convert excel time (days since 1900) to time_t and back again */ + +#define EXCEL_TO_TIMET(a) ((a - 25569.0) * 86400.0) +#define TIMET_TO_EXCEL(a) ((a / 86400.0) + 25569.0) + +/* Bitmaps */ + +typedef struct { + int id; + char *name; +} raymarine_symbol_mapping_t; + +static raymarine_symbol_mapping_t raymarine_symbols[] = { + { 0, "Unknown Symbol 0" }, + { 1, "Unknown Symbol 1" }, + { 2, "Unknown Symbol 2" }, + { 3, "Red Square" }, + { 4, "Big Fish" }, + { 5, "Anchor" }, + { 6, "Smiley" }, + { 7, "Sad" }, + { 8, "Red Button" }, + { 9, "Sailfish" }, + { 10, "Danger" }, + { 11, "Attention" }, + { 12, "Black Square" }, + { 13, "Intl. Dive Flag" }, + { 14, "Vessel" }, + { 15, "Lobster" }, + { 16, "Buoy" }, + { 17, "Exclamation" }, + { 18, "Red X" }, + { 19, "Check Mark" }, + { 20, "Black Plus" }, + { 21, "Black Cross" }, + { 22, "MOB" }, + { 23, "Billfish" }, + { 24, "Bottom Mark" }, + { 25, "Circle" }, + { 26, "Diamond" }, + { 27, "Diamond Quarters" }, + { 28, "U.S.Dive Flag" }, + { 29, "Dolphin" }, + { 30, "Few Fish" }, + { 31, "Multiple Fish" }, + { 32, "Many Fish" }, + { 33, "Single Fish" }, + { 34, "Small Fish" }, + { 35, "Marker" }, + { 36, "Cocktails" }, + { 37, "Red Box Marker" }, + { 38, "Reef" }, + { 39, "Rocks" }, + { 40, "FishSchool" }, + { 41, "Seaweed" }, + { 42, "Shark" }, + { 43, "Sportfisher" }, + { 44, "Swimmer" }, + { 45, "Top Mark" }, + { 46, "Trawler" }, + { 47, "Tree" }, + { 48, "Triangle" }, + { 49, "Wreck" } +}; + +#define RAYMARINE_SYMBOL_CT sizeof(raymarine_symbols) / sizeof(raymarine_symbol_mapping_t) +#define RAYMARINE_STD_SYMBOL 3 + +static int +find_symbol_num(const char *descr) +{ + if ((descr != NULL) && (*descr)) { + + int i; + raymarine_symbol_mapping_t *a; + + a = &raymarine_symbols[0]; + + for (i = 0; i < RAYMARINE_SYMBOL_CT; i++, a++) { + printf(MYNAME ": %s - %s\n", descr, a->name); + if (case_ignore_strcmp(descr, a->name) == 0) return i; + } + } + + return RAYMARINE_STD_SYMBOL; +} + +/* ============================================= */ +/* %%% R A Y M A R I N E R E A D E R %%% */ +/* ============================================= */ + +static void +raymarine_rd_init(const char *fname) +{ + fin = inifile_init(fname, MYNAME); +} + +static void +raymarine_rd_done(void) +{ + inifile_done(fin); +} + +static void +raymarine_read(void) +{ + waypoint *wpt; + int ix, rx; + + /* Read all waypoints */ + + for (ix = 0; ix < 0x3FFF; ix++) { + char sect[10]; + char *str, *name, *lat, *lon; + + /* built section identifier */ + snprintf(sect, sizeof(sect), "Wp%d", ix); + + /* try to read our most expected values */ + if (NULL == (name = inifile_readstr(fin, sect, "Name"))) break; + if (NULL == (lat = inifile_readstr(fin, sect, "Lat"))) break; + if (NULL == (lon = inifile_readstr(fin, sect, "Long"))) break; + + wpt = waypt_new(); + wpt->shortname = xstrdup(name); + wpt->latitude = atof(lat); + wpt->longitude = atof(lon); + waypt_add(wpt); + + /* try to read optional values */ + if (((str = inifile_readstr(fin, sect, "Notes"))) && *str) wpt->notes = xstrdup(str); + if (((str = inifile_readstr(fin, sect, "Time"))) && *str) wpt->creation_time = EXCEL_TO_TIMET(atof(str)); + if (((str = inifile_readstr(fin, sect, "Bmp"))) && *str) { + int symbol = atoi(str); + + if ((symbol < 3) && (symbol >= RAYMARINE_SYMBOL_CT)) + symbol = RAYMARINE_STD_SYMBOL; + wpt->icon_descr = raymarine_symbols[symbol].name; + } + } + + /* Read all routes */ + + for (rx = 0; rx < 0x3FFF; rx++) { + char sect[10]; + char *name; + route_head *rte; + int wx; + + snprintf(sect, sizeof(sect), "Rt%d", rx); + if (NULL == (name = inifile_readstr(fin, sect, "Name"))) break; + + rte = route_head_alloc(); + rte->rte_name = xstrdup(name); + route_add_head(rte); + + for (wx = 0; wx < 0x3FFF; wx++) { + char buff[32]; + char *str; + waypoint * wpt; + + snprintf(buff, sizeof(buff), "Mk%d", wx); + str = inifile_readstr(fin, sect, buff); + if ((str == NULL) || (*str == '\0')) break; + + wpt = find_waypt_by_name(str); + if (wpt == NULL) + fatal(MYNAME ": No associated waypoint for route point %s (Route %s)!\n", + str, rte->rte_name); + + route_add_wpt(rte, waypt_dupe(wpt)); + } + } +} + +/* ============================================= */ +/* %%% R A Y M A R I N E W R I T E R %%% */ +/* ============================================= */ + + +static guid_t +mkGUID(void) +{ + guid_t res; + + if (gpsbabel_now != 0) { + srand(gpsbabel_now + rand()); + res = ((guid_t) (gpsbabel_now) << 48) | + ((guid_t)(rand() & 0xFFFF) << 32) | + ((guid_t)(rand() & 0xFFFF) << 16) | + (rand() & 0xFFFF); + } + else res = 0; + + return res; +} + +static char * +GUID2str(guid_t GUID) +{ + gbuint16 w0, w1, w2, w3; + char *res; + + w0 = GUID & 0xFFFF; GUID = GUID >> 16; + w1 = GUID & 0xFFFF; GUID = GUID >> 16; + w2 = GUID & 0xFFFF; GUID = GUID >> 16; + w3 = GUID & 0xFFFF; + + xasprintf(&res, "%d-%d-%d-%d", w0, w1, w2, w3); + return res; +} + + +static void +register_waypoint(const waypoint *wpt) +{ + int i; + + for (i = 0; i < items_in_depot; i++) { + waypoint *cmp = depot[i]; + if ((strcmp(wpt->shortname, cmp->shortname) == 0) && + (wpt->latitude == cmp->latitude) && + (wpt->longitude == cmp->longitude)) + return; + } + + if (items_in_depot >= size_of_depot) { + size_of_depot+=16; + depot = (void *) xrealloc(depot, size_of_depot * sizeof(wpt)); + } + + depot[items_in_depot] = (waypoint *)wpt; + items_in_depot++; +} + +static void +write_waypoint(gbfile *fout, const waypoint *wpt, const int waypt_no, const char *location, const char *GUID) +{ + char *notes; + char *temp; + double time; + + /* ToDo: remove possible line-breaks from notes */ + + notes = (wpt->notes != NULL) ? wpt->notes : ""; + time = (wpt->creation_time != 0) ? TIMET_TO_EXCEL(wpt->creation_time) : TIMET_TO_EXCEL(gpsbabel_time); + + temp = mkshort(hshort, wpt->shortname); + gbfprintf(fout, "[Wp%d]\n" + "Loc=%s\n" + "Name=%s\n" + "Lat=%.15f\n" + "Long=%.15f\n", + waypt_no, location, temp, wpt->latitude, wpt->longitude + ); + xfree(temp); + gbfprintf(fout, "Rng=%.15f\n" + "Bear=%.15f\n" + "Bmp=%d\n" + "Fixed=1\n" + "Locked=0\n" + "Notes=%s\n", + 0.0, 0.0, + find_symbol_num(wpt->icon_descr), + notes + ); + gbfprintf(fout, "Rel=\n" + "RelSet=1\n" + "RcCount=1\n" + "RcRadius=%.15f\n" + "Show=1\n" + "RcShow=0\n" + "SeaTemp=%.15f\n" + "Depth=%.15f\n" + "Time=%.15f\n" + "GUID=%s\n", + 0.0, -32678.0, 65535.0, time, GUID + ); +} + +static void +write_route_head_cb(const route_head *rte) +{ + gbfprintf(fout, "[Rt%d]\n" + "Name=%s\n" + "Visible=1\n" + "Guid=%s\n", + rte_index, + rte->rte_name, + sguid + ); + rte_index++; + rte_wpt_index = 0; +} + +static void +write_route_wpt_cb(const waypoint *wpt) +{ + int i; + char *s; + static char *items[] = { + "Cog", + "Eta", + "Length" + "PredictedDrift", + "PredictedSet", + "PredictedSog", + "PredictedTime", + "PredictedTwa2", + "PredictedTwd", + "PredictedTws" }; + + s = mkshort(hshort, wpt->shortname); + gbfprintf(fout, "Mk%d=%s\n", rte_wpt_index, wpt->shortname); + xfree(s); + + for (i = 0; i < sizeof(items) / sizeof(char *); i++) { + gbfprintf(fout, "%s%d=%.15f\n", items[i], rte_wpt_index, 0.0); + } + rte_wpt_index++; +} + +static void +enum_route_hdr_cb(const route_head *rte) +{ + is_fatal(rte->rte_waypt_ct > 50, + MYNAME ": Routes with more than 50 points are not supported by Waymarine!"); +} + +static void +enum_route_wpt_cb(const waypoint *wpt) +{ + register_waypoint(wpt); +} + +static void +raymarine_wr_init(const char *fname) +{ + fout = gbfopen(fname, "w", MYNAME); + + hshort = mkshort_new_handle(); + setshort_length(hshort, 16); + + setshort_badchars(hshort, ","); + setshort_mustupper(hshort, 0); + setshort_mustuniq(hshort, 1); + setshort_whitespace_ok(hshort, 1); + setshort_repeating_whitespace_ok(hshort, 1); +} + +static void +raymarine_wr_done(void) +{ + mkshort_del_handle(&hshort); + gbfclose(fout); +} + +static void +raymarine_write(void) +{ + int i; + queue *elem, *tmp; + extern queue waypt_head; + guid_t guid; + + size_of_depot = 0; + items_in_depot = 0; + depot = NULL; + guid = mkGUID(); + sguid = GUID2str(guid); + + /* enumerate all possible waypoints */ + QUEUE_FOR_EACH(&waypt_head, elem, tmp) { + waypoint *wpt = (waypoint *) elem; + register_waypoint(wpt); + } + route_disp_all(enum_route_hdr_cb, NULL, enum_route_wpt_cb); + + /* write out waypoint summary */ + for (i = 0; i < items_in_depot; i++) { + waypoint *wpt = depot[i]; + write_waypoint(fout, wpt, i, opt_location, sguid); + } + + /* write out all routes with their waypoints */ + rte_index = 0; + route_disp_all(write_route_head_cb, NULL, write_route_wpt_cb); + + if (depot != NULL) xfree(depot); + xfree(sguid); +} + +/* ================================================== */ +/* %%% M O D U L E R E G I S T R A T I O N %%% */ +/* ================================================== */ + +ff_vecs_t raymarine_vecs = { + ff_type_file, + { + ff_cap_read | ff_cap_write /* waypoints */, + ff_cap_none /* tracks */, + ff_cap_read | ff_cap_write /* routes */, + }, + raymarine_rd_init, + raymarine_wr_init, + raymarine_rd_done, + raymarine_wr_done, + raymarine_read, + raymarine_write, + NULL, + raymarine_args, + CET_CHARSET_ASCII, 0 +}; diff --git a/reference/raymarine-sample.gpx b/reference/raymarine-sample.gpx new file mode 100644 index 000000000..cb130d37b --- /dev/null +++ b/reference/raymarine-sample.gpx @@ -0,0 +1,83 @@ + + + + + + + Qr1-one + Qr1-one + notes-1 + Red Square + + + + Qr,2 + Qr,2 + notes-2 + Big Fish + + + + Qr3 + Qr3 + notes-3 + Anchor + + + + Qr4-four + Qr4-four + notes-4 + Smiley + + + + Qr5 + Qr5 + notes-5 + Sad + + + Qr + + + Qr1-one + Qr1-one + notes-1 + Red Square + + + + Qr,2 + Qr,2 + notes-2 + Big Fish + + + + Qr3 + Qr3 + notes-3 + Anchor + + + + Qr4-four + Qr4-four + notes-4 + Smiley + + + + Qr5 + Qr5 + notes-5 + Sad + + + diff --git a/reference/raymarine-sample.rwf b/reference/raymarine-sample.rwf new file mode 100644 index 000000000..5657d289d --- /dev/null +++ b/reference/raymarine-sample.rwf @@ -0,0 +1,164 @@ +[Wp0] +Loc=Qr +Name=Qr1-one +Lat=51.593568734471624 +Long=9.895914981396029 +Rng=0.000000000000000 +Bear=0.000000000000000 +Bmp=3 +Fixed=1 +Locked=0 +Notes=notes-1 +Rel= +RelSet=1 +RcCount=1 +RcRadius=0.000000000000000 +Show=0 +RcShow=0 +SeaTemp=-32678.000000000000000 +Depth=65535.000000000000000 +Time=39027.880335648151000 +GUID=41847-17743-33206-33476 +[Wp1] +Loc=Qr +Name=Qr,2 +Lat=50.833425047658316 +Long=10.364770733812209 +Rng=0.000000000000000 +Bear=0.000000000000000 +Bmp=4 +Fixed=1 +Locked=0 +Notes=notes-2 +Rel= +RelSet=1 +RcCount=1 +RcRadius=0.000000000000000 +Show=0 +RcShow=0 +SeaTemp=-32678.000000000000000 +Depth=65535.000000000000000 +Time=39027.880347222221000 +GUID=41847-17743-33462-33476 +[Wp2] +Loc=Qr +Name=Qr3 +Lat=50.184475273537196 +Long=10.557828984807108 +Rng=0.000000000000000 +Bear=0.000000000000000 +Bmp=5 +Fixed=1 +Locked=0 +Notes=notes-3 +Rel= +RelSet=1 +RcCount=1 +RcRadius=0.000000000000000 +Show=0 +RcShow=0 +SeaTemp=-32678.000000000000000 +Depth=65535.000000000000000 +Time=39027.880347222221000 +GUID=41847-17743-33718-33476 +[Wp3] +Loc=Qr +Name=Qr4-four +Lat=48.986577919201942 +Long=12.047135492482036 +Rng=0.000000000000000 +Bear=0.000000000000000 +Bmp=6 +Fixed=1 +Locked=0 +Notes=notes-4 +Rel= +RelSet=1 +RcCount=1 +RcRadius=0.000000000000000 +Show=0 +RcShow=0 +SeaTemp=-32678.000000000000000 +Depth=65535.000000000000000 +Time=39027.880347222221000 +GUID=41847-17743-33974-33476 +[Wp4] +Loc=Qr +Name=Qr5 +Lat=48.623286046917791 +Long=12.598730495324602 +Rng=0.000000000000000 +Bear=0.000000000000000 +Bmp=7 +Fixed=1 +Locked=0 +Notes=notes-5 +Rel= +RelSet=1 +RcCount=1 +RcRadius=0.000000000000000 +Show=0 +RcShow=0 +SeaTemp=-32678.000000000000000 +Depth=65535.000000000000000 +Time=39027.880358796298000 +GUID=41847-17743-34230-33476 +[Rt0] +Name=Qr +Visible=0 +Guid=41847-17743-32950-33476 +Mk0=Qr1-one +Cog0=0.000000000000000 +Eta0=0.000000000000000 +Length0=0.000000000000000 +PredictedDrift0=0.000000000000000 +PredictedSet0=0.000000000000000 +PredictedSog0=0.000000000000000 +PredictedTime0=0.000000000000000 +PredictedTwa0=0.000000000000000 +PredictedTwd0=0.000000000000000 +PredictedTws0=0.000000000000000 +Mk1=Qr,2 +Cog1=0.000000000000000 +Eta1=0.000000000000000 +Length1=0.000000000000000 +PredictedDrift1=0.000000000000000 +PredictedSet1=0.000000000000000 +PredictedSog1=0.000000000000000 +PredictedTime1=0.000000000000000 +PredictedTwa1=0.000000000000000 +PredictedTwd1=0.000000000000000 +PredictedTws1=0.000000000000000 +Mk2=Qr3 +Cog2=0.000000000000000 +Eta2=0.000000000000000 +Length2=0.000000000000000 +PredictedDrift2=0.000000000000000 +PredictedSet2=0.000000000000000 +PredictedSog2=0.000000000000000 +PredictedTime2=0.000000000000000 +PredictedTwa2=0.000000000000000 +PredictedTwd2=0.000000000000000 +PredictedTws2=0.000000000000000 +Mk3=Qr4-four +Cog3=0.000000000000000 +Eta3=0.000000000000000 +Length3=0.000000000000000 +PredictedDrift3=0.000000000000000 +PredictedSet3=0.000000000000000 +PredictedSog3=0.000000000000000 +PredictedTime3=0.000000000000000 +PredictedTwa3=0.000000000000000 +PredictedTwd3=0.000000000000000 +PredictedTws3=0.000000000000000 +Mk4=Qr5 +Cog4=0.000000000000000 +Eta4=0.000000000000000 +Length4=0.000000000000000 +PredictedDrift4=0.000000000000000 +PredictedSet4=0.000000000000000 +PredictedSog4=0.000000000000000 +PredictedTime4=0.000000000000000 +PredictedTwa4=0.000000000000000 +PredictedTwd4=0.000000000000000 +PredictedTws4=0.000000000000000 diff --git a/testo b/testo index d43841c42..8cf5b64f3 100755 --- a/testo +++ b/testo @@ -1150,5 +1150,11 @@ compare ${TMPDIR}/transform-rte.gpx ${REFERENCE}/transform-rte.gpx ${PNAME} -i gpx -f ${REFERENCE}/expertgps.gpx -x nuketypes,waypoints,tracks -x transform,wpt=rte,del=y -o gpx -F ${TMPDIR}/transform-wpt.gpx compare ${TMPDIR}/transform-wpt.gpx ${REFERENCE}/transform-wpt.gpx +# +# "Raymarine Waypoint File" tests +# +${PNAME} -i raymarine -f ${REFERENCE}/raymarine-sample.rwf -o gpx -F ${TMPDIR}/raymarine-sample.gpx +compare ${TMPDIR}/raymarine-sample.gpx ${REFERENCE}/raymarine-sample.gpx + exit 0 diff --git a/vecs.c b/vecs.c index 7d3befe36..f8520a232 100644 --- a/vecs.c +++ b/vecs.c @@ -111,6 +111,7 @@ extern ff_vecs_t wbt_svecs; extern ff_vecs_t wbt_fvecs; extern ff_vecs_t gtc_vecs; extern ff_vecs_t dmtlog_vecs; +extern ff_vecs_t raymarine_vecs; static vecs_t vec_list[] = { @@ -607,6 +608,12 @@ vecs_t vec_list[] = { "TrackLogs digital mapping (.trl)", "trl" }, + { + &raymarine_vecs, + "raymarine", + "Raymarine Waypoint File (.rwf)", + "rwf" + }, { NULL, NULL, diff --git a/xmldoc/formats/raymarine.xml b/xmldoc/formats/raymarine.xml new file mode 100644 index 000000000..258c12edf --- /dev/null +++ b/xmldoc/formats/raymarine.xml @@ -0,0 +1,7 @@ + + This format supports the "Raymarine Waypoint File" format (.rwf). + More information to Raymarine you'll find at their homepage. + + + Known limits: max. 16 characters for waypoint names and max. 50 waypoints per route. + -- 2.30.2